﻿using OpenCvSharp.Internal;
using OpenCvSharp.Internal.Vectors;

// ReSharper disable UnusedMember.Global

namespace OpenCvSharp.XImgProc.Segmentation;

/// <summary>
/// Selective search segmentation algorithm.
/// The class implements the algorithm described in @cite uijlings2013selective.
/// </summary>
public class SelectiveSearchSegmentation : Algorithm
{
    private Ptr? ptrObj;

    /// <summary>
    /// Creates instance by raw pointer
    /// </summary>
    protected SelectiveSearchSegmentation(IntPtr p)
    {
        ptrObj = new Ptr(p);
        ptr = ptrObj.Get();
    }

    /// <summary>
    /// Releases managed resources
    /// </summary>
    protected override void DisposeManaged()
    {
        ptrObj?.Dispose();
        ptrObj = null;
        base.DisposeManaged();
    }

    /// <summary>
    /// Create a new SelectiveSearchSegmentation class.
    /// </summary>
    /// <returns></returns>
    public static SelectiveSearchSegmentation Create()
    {
        NativeMethods.HandleException(
            NativeMethods.ximgproc_segmentation_createSelectiveSearchSegmentation(out var p));
        return new SelectiveSearchSegmentation(p);
    }
        
    /// <summary>
    /// Set a image used by switch* functions to initialize the class
    /// </summary>
    /// <param name="img">The image</param>
    public virtual void SetBaseImage(InputArray img)
    {
        ThrowIfDisposed();
        if (img is null)
            throw new ArgumentNullException(nameof(img));
        img.ThrowIfDisposed();

        NativeMethods.HandleException(
            NativeMethods.ximgproc_segmentation_SelectiveSearchSegmentation_setBaseImage(ptr, img.CvPtr));

        GC.KeepAlive(this);
        GC.KeepAlive(img);
    }

    /// <summary>
    /// Initialize the class with the 'Single stragegy' parameters describled in @cite uijlings2013selective.
    /// </summary>
    /// <param name="k">The k parameter for the graph segmentation</param>
    /// <param name="sigma">The sigma parameter for the graph segmentation</param>
    public virtual void SwitchToSingleStrategy(int k = 200, float sigma = 0.8f)
    {
        ThrowIfDisposed();
        NativeMethods.HandleException(
            NativeMethods.ximgproc_segmentation_SelectiveSearchSegmentation_switchToSingleStrategy(ptr, k, sigma));
        GC.KeepAlive(this);
    }

    /// <summary>
    /// Initialize the class with the 'Selective search fast' parameters describled in @cite uijlings2013selective.
    /// </summary>
    /// <param name="baseK">The k parameter for the first graph segmentation</param>
    /// <param name="incK">The increment of the k parameter for all graph segmentations</param>
    /// <param name="sigma">The sigma parameter for the graph segmentation</param>
    public virtual void SwitchToSelectiveSearchFast(int baseK = 150, int incK = 150, float sigma = 0.8f)
    {
        ThrowIfDisposed();
        NativeMethods.HandleException(
            NativeMethods.ximgproc_segmentation_SelectiveSearchSegmentation_switchToSelectiveSearchFast(ptr, baseK, incK, sigma));
        GC.KeepAlive(this);
    }

    /// <summary>
    /// Initialize the class with the 'Selective search fast' parameters describled in @cite uijlings2013selective.
    /// </summary>
    /// <param name="baseK">The k parameter for the first graph segmentation</param>
    /// <param name="incK">The increment of the k parameter for all graph segmentations</param>
    /// <param name="sigma">The sigma parameter for the graph segmentation</param>
    public virtual void SwitchToSelectiveSearchQuality(int baseK = 150, int incK = 150, float sigma = 0.8f)
    {
        ThrowIfDisposed();
        NativeMethods.HandleException(
            NativeMethods.ximgproc_segmentation_SelectiveSearchSegmentation_switchToSelectiveSearchQuality(ptr, baseK, incK, sigma));
        GC.KeepAlive(this);
    }

    /// <summary>
    /// Add a new image in the list of images to process.
    /// </summary>
    /// <param name="img">The image</param>
    public virtual void AddImage(InputArray img)
    {
        ThrowIfDisposed();
        if (img is null)
            throw new ArgumentNullException(nameof(img));
        img.ThrowIfDisposed();

        NativeMethods.HandleException(
            NativeMethods.ximgproc_segmentation_SelectiveSearchSegmentation_addImage(ptr, img.CvPtr));

        GC.KeepAlive(this);
        GC.KeepAlive(img);
    }

    /// <summary>
    /// Clear the list of images to process
    /// </summary>
    public virtual void ClearImages()
    {
        ThrowIfDisposed();
        NativeMethods.HandleException(
            NativeMethods.ximgproc_segmentation_SelectiveSearchSegmentation_clearImages(ptr));
        GC.KeepAlive(this);
    }

    /// <summary>
    /// Add a new graph segmentation in the list of graph segmentations to process.
    /// </summary>
    /// <param name="g">The graph segmentation</param>
    public virtual void AddGraphSegmentation(GraphSegmentation g)
    {
        ThrowIfDisposed();
        if (g is null)
            throw new ArgumentNullException(nameof(g));
        g.ThrowIfDisposed();

        if (g.PtrObj is null)
            throw new ArgumentException("PtrObj = null", nameof(g));

        NativeMethods.HandleException(
            NativeMethods.ximgproc_segmentation_SelectiveSearchSegmentation_addGraphSegmentation(ptr, g.PtrObj.CvPtr));

        GC.KeepAlive(this);
        GC.KeepAlive(g);
    }

    /// <summary>
    /// Clear the list of graph segmentations to process
    /// </summary>
    public virtual void ClearGraphSegmentations()
    {
        ThrowIfDisposed();
        NativeMethods.HandleException(
            NativeMethods.ximgproc_segmentation_SelectiveSearchSegmentation_clearGraphSegmentations(ptr));
        GC.KeepAlive(this);
    }

    /// <summary>
    /// Add a new strategy in the list of strategy to process.
    /// </summary>
    /// <param name="s">The strategy</param>
    public virtual void AddStrategy(SelectiveSearchSegmentationStrategy s)
    {
        ThrowIfDisposed();
        if (s is null)
            throw new ArgumentNullException(nameof(s));
        s.ThrowIfDisposed();
        if (s.PtrObj is null)
            throw new ArgumentException("s.PtrObj is null");

        NativeMethods.HandleException(
            NativeMethods.ximgproc_segmentation_SelectiveSearchSegmentation_addStrategy(ptr, s.PtrObj.CvPtr));

        GC.KeepAlive(this);
        GC.KeepAlive(s);
    }

    /// <summary>
    /// Clear the list of strategy to process;
    /// </summary>
    public virtual void ClearStrategies()
    {
        ThrowIfDisposed();
        NativeMethods.HandleException(
            NativeMethods.ximgproc_segmentation_SelectiveSearchSegmentation_clearStrategies(ptr));
        GC.KeepAlive(this);
    }

    /// <summary>
    /// Based on all images, graph segmentations and stragies, computes all possible rects and return them
    /// </summary>
    /// <param name="rects">The list of rects. The first ones are more relevents than the lasts ones.</param>
    public virtual void Process(out Rect[] rects)
    {
        ThrowIfDisposed();

        using var rectsVec = new VectorOfRect();
        NativeMethods.HandleException(
            NativeMethods.ximgproc_segmentation_SelectiveSearchSegmentation_process(ptr, rectsVec.CvPtr));
        rects = rectsVec.ToArray();

        GC.KeepAlive(this);
    }

    internal class Ptr(IntPtr ptr) : OpenCvSharp.Ptr(ptr)
    {
        public override IntPtr Get()
        {
            NativeMethods.HandleException(
                NativeMethods.ximgproc_segmentation_Ptr_SelectiveSearchSegmentation_get(ptr, out var ret));
            GC.KeepAlive(this);
            return ret;
        }

        protected override void DisposeUnmanaged()
        {
            NativeMethods.HandleException(
                NativeMethods.ximgproc_segmentation_Ptr_SelectiveSearchSegmentation_delete(ptr));
            base.DisposeUnmanaged();
        }
    }
}
